package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

//import static com.atguigu.tingshu.common.constant.SystemConstant.ALBUM_PAY_TYPE_VIPFREE;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private VodUploadClient vodUploadClient;

    @Autowired
    private VodConstantProperties props;

    /**
     * 将音视频文件上传腾讯云点播平台
     *
     * @param file 文件
     * @return {"mediaFileId":"音视频文件唯一标识","mediaUrl":"音视频文件播放地址"}
     */
    @Override
    public Map<String, String> uploadTrack(MultipartFile file) {
        try {
            //1.将上传文件保存到临时目录： D:\code\workspace2023\tingshu\temp
            String tempFilePath = UploadFileUtil.uploadTempPath(props.getTempPath(), file);
            //2.创建文件上传请求对象
            VodUploadRequest request = new VodUploadRequest();
            //2.1 音频文件本地路径
            request.setMediaFilePath(tempFilePath);
            //2.2 指定当前音视频文件封面图片
            request.setCoverFilePath("");
            //3.调用点播平台上传文件接口
            VodUploadResponse uploadResponse = vodUploadClient.upload(props.getRegion(), request);
            if (uploadResponse != null) {
                Map<String, String> map = new HashMap<>();
                map.put("mediaFileId", uploadResponse.getFileId());
                //4.TODO 开启审核任务（点播平台启动任务进行审核-耗时较长）-作业 异步审核 https://console.cloud.tencent.com/api/explorer?Product=vod&Version=2018-07-17&Action=ReviewAudioVideo
                // 得到审核任务ID，后续根据任务ID查询审核任务 https://console.cloud.tencent.com/api/explorer?Product=vod&Version=2018-07-17&Action=DescribeTaskDetail
                map.put("mediaUrl", uploadResponse.getMediaUrl());
                return map;
            }
        } catch (Exception e) {
            log.error("上传音频文件异常", e);
            throw new GuiguException(500, "上传音频文件异常！");
        }
        return null;
    }

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private VodService vodService;

    /**
     * 保存声音
     *
     * @param userId
     * @param trackInfoVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(Long userId, TrackInfoVo trackInfoVo) {
        //0.根据专辑ID查询专辑信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());
        //1.保存声音记录
        //1.1 将声音VO对象转为PO对象
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
        //1.2 为声音PO对象中某些属性赋值
        //1.2.1 用户ID
        trackInfo.setUserId(userId);
        //1.2.2 当前新增声音序号
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
        //1.2.3 调用云点播平台获取 音视频时长、大小、类型、审核状态
        TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
        if (trackMediaInfoVo != null) {
            trackInfo.setMediaDuration(new BigDecimal(trackMediaInfoVo.getDuration()));
            trackInfo.setMediaType(trackMediaInfoVo.getType());
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
        }
        //1.2.4 审核状态 专辑发布时候统一对专辑下所有声音进行审核状态校验
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);

        //1.3 保存声音记录、
        trackInfoMapper.insert(trackInfo);

        //2.新增声音统计记录
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT, 0);

        //3.修改专辑包含声音数量
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        albumInfoMapper.updateById(albumInfo);

        //TODO 4.对上传的文件进行审核
        String reviewMediaTaskId = vodService.startReviewMediaTask(trackInfo.getMediaFileId());
        trackInfo.setReviewTaskId(reviewMediaTaskId);
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
        trackInfoMapper.updateById(trackInfo);
    }

    @Autowired
    private TrackStatMapper trackStatMapper;

    /**
     * 保存声音统计信息
     *
     * @param trackId
     * @param statType
     * @param num
     */
    @Override
    public void saveTrackStat(Long trackId, String statType, int num) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(num);
        trackStatMapper.insert(trackStat);
    }

    /**
     * 分页获取声音列表
     *
     * @param pageInfo       分页对象
     * @param trackInfoQuery 查询条件
     * @return
     */
    @Override
    public Page<TrackListVo> getUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery) {
        return trackInfoMapper.getUserTrackPage(pageInfo, trackInfoQuery);
    }

    /**
     * 删除声音
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeTrackInfo(Long id) {
        //1.更新专辑及声音查询当前被删除声音对象得到专辑ID，跟当前声音在专辑中序号
        TrackInfo deleteTrackInfo = trackInfoMapper.selectById(id);
        Long albumId = deleteTrackInfo.getAlbumId();
        Integer orderNum = deleteTrackInfo.getOrderNum();

        //2.删除声音
        trackInfoMapper.deleteById(id);

        //3.删除声音统计信息
        LambdaQueryWrapper<TrackStat> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackStat::getTrackId, id);
        trackStatMapper.delete(queryWrapper);

        //4.声音列表中大于当前序号进行-1操作
        trackInfoMapper.updateOrderNum(albumId, orderNum);

        //5.更新专辑中声音数量
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);

        //6.将腾讯点播平台上音频文件删除
        vodService.deleteMedia(deleteTrackInfo.getMediaFileId());
    }

    /**
     * 更新声音信息
     *
     * @param id
     * @param trackInfo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackInfo(TrackInfo trackInfo) {
        //1.根据声音ID查询声音对象 得到“旧”音频唯一标识
        TrackInfo oldTracInfo = trackInfoMapper.selectById(trackInfo.getId());
        String oldMediaFileId = oldTracInfo.getMediaFileId();

        //2.判断音频文件是否被修改 ，如果被修改：更新声音交流中新音频信息，从点播平台删除旧音频文件
        if (!oldMediaFileId.equals(trackInfo.getMediaFileId())) {
            //2.1 根据最新音频文件标识查询腾讯点播平台
            TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(trackInfo.getMediaFileId());
            if (mediaInfo != null) {
                trackInfo.setMediaType(mediaInfo.getType());
                trackInfo.setMediaDuration(new BigDecimal(mediaInfo.getDuration()));
                trackInfo.setMediaSize(mediaInfo.getSize());
                //2.2 删除点播平台旧文件
                vodService.deleteMedia(oldMediaFileId);
                //TODO 2.3 新上传的文件同样需要进行审核
                vodService.startReviewMediaTask(trackInfo.getMediaFileId());
                trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
            }
        }
        //2.2 修改声音信息
        trackInfoMapper.updateById(trackInfo);
    }

    @Autowired
    private UserFeignClient userFeignClient;

    /**
     * 分页展示专辑下声音列表-动态显示付费标识
     * 根据用户登录状态、身份、专辑付费类型、购买情况综合判断付费标识
     *
     * @param pageInfo 分页对象
     * @param userId   用户ID
     * @param albumId  专辑ID
     * @return
     */
    @Override
    public Page<AlbumTrackListVo> getAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long userId, Long albumId) {
        //1.获取指定专辑下包含声音分页列表（不考虑付费标识 AlbumTrackListVo中付费标识：false）
        pageInfo = trackInfoMapper.getAlbumTrackPage(pageInfo, albumId);
        //TODO 根据用户登录状态、身份、专辑付费类型、购买情况综合判断付费标识
        //2. 根据专辑ID查询专辑信息-获取专辑付费类型-付费类型: 0101-免费、0102-vip免费、0103-付费
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        //2.1 获取专辑付费类型
        String payType = albumInfo.getPayType();
        //2.2 获取专辑免费试听集数
        Integer tracksForFreeCount = albumInfo.getTracksForFree();

        //3. 处理用户未登录情况
        if (userId == null) {
            //3.1 判断专辑付费类型是否为VIP免费或者付费
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                //3.2 将本页中声音列表除去免费试听声音将声音付费标识设置为：true
                pageInfo.getRecords()
                        .stream()
                        .filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > tracksForFreeCount) //去除免费试听声音
                        .forEach(albumTrackListVo -> {
                            albumTrackListVo.setIsShowPaidMark(true);
                        });
            }
        } else {
            //4. 处理用户已登录情况
            //4.1 远程调用用户服务获取用户信息，得到用户身份
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "用户：{}不存在", userId);
            Boolean isVIP = false;
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }
            //4.2 判断专辑付费类型-是否需要进一步判断用户购买情况
            Boolean isNeedCheck = false;
            //4.2.1 判断专辑付费类型是否为：VIP免费
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)) {
                if (!isVIP) {
                    //当前用户为普通用户 专辑类型为VIP免费 需要进一步判断购买情况
                    isNeedCheck = true;
                }
            }
            //4.2.2 判断专辑付费类型是否为：付费-所有用户都需要进一步判断购买情况
            if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                isNeedCheck = true;
            }

            //4.3 需要进一步判断购买情况
            if (isNeedCheck) {
                //4.3.1 将免费试听声音排除掉得到需要判断购买情况声音ID集合
                List<Long> needCheckBuyStausTrackIdList = pageInfo
                        .getRecords()
                        .stream()
                        .filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > tracksForFreeCount) //去除免费试听声音
                        .map(AlbumTrackListVo::getTrackId) //映射获取声音ID
                        .collect(Collectors.toList());    //收集得到需要判断购买情况声音ID集合
                //4.3.2 远程调用用户服务判断本页中声音ID列表购买情况 {声音ID:1/0}
                Map<Long, Integer> buyStatusMap = userFeignClient.userIsPaidTrack(userId, albumId, needCheckBuyStausTrackIdList).getData();
                //4.3.3 根据返回购买情况Map，判断是否需要将标识改为true（未购买声音）
                pageInfo
                        .getRecords()
                        .stream()
                        .filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > tracksForFreeCount)
                        .forEach(albumTrackListVo -> {
                            //找出本页中未购买声音ID，将付费标识设置：true
                            if (buyStatusMap.get(albumTrackListVo.getTrackId()).intValue() == 0) {
                                albumTrackListVo.setIsShowPaidMark(true);
                            }
                        });
            }
        }
        return pageInfo;
    }

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 更新声音统计信息
     * 1.避免同一个统计消息被重复消息
     * 2.事务管理
     *
     * @param trackStatMqVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackStat(TrackStatMqVo trackStatMqVo) {
        //1.避免同一个统计消息被重复消息，利用Redis命令set k v [ex][nx]某次统计消息只有第一次能存入成功
        //1.1 创建key
        String key = RedisConstant.BUSINESS_PREFIX + "album:" + trackStatMqVo.getBusinessNo();
        //1.2 尝试存入redis
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "", 10, TimeUnit.MINUTES);
        if (flag) {
            try {
                //2.更新声音统计信息
                trackInfoMapper.updateStat(trackStatMqVo.getTrackId(), trackStatMqVo.getStatType(), trackStatMqVo.getCount());

                //3.更新专辑统计信息 当前方法当声音统计操作发生后才会被调用，专辑中存在跟声音相同统计类型：播放量，评论量
                if (SystemConstant.TRACK_STAT_PLAY.equals(trackStatMqVo.getStatType())) {
                    //3.1 同时更新专辑播放量
                    albumInfoMapper.updateStat(trackStatMqVo.getAlbumId(), SystemConstant.ALBUM_STAT_PLAY, trackStatMqVo.getCount());
                }
                if (SystemConstant.TRACK_STAT_COMMENT.equals(trackStatMqVo.getStatType())) {
                    //3.2 同时更新专辑评论量
                    albumInfoMapper.updateStat(trackStatMqVo.getAlbumId(), SystemConstant.ALBUM_STAT_COMMENT, trackStatMqVo.getCount());
                }
            } catch (Exception e) {
                //4.如果更新统计发生异常，确保消费者重试能够再次setnx到Redis成功
                redisTemplate.delete(key);
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 根据选择待购声音作为购买起始声音得到分集购买列表
     *
     * @param trackId 选择付费标识声音-作为起始购买声音
     * @return [{name:"本集",price:1,trackCount:1},{name:"后10集",price:10,trackCount:10},{}.,{name:"全集",price:33,trackCount:33}]
     */
    @Override
    public List<Map<String, Object>> getFenJiBuyList(Long userId, Long trackId) {
        //1.根据起始购买声音ID查询选中待购声音信息-得到专辑ID，声音序号
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Assert.notNull(trackId, "选择声音有误！");
        Long albumId = trackInfo.getAlbumId();
        Integer orderNum = trackInfo.getOrderNum();
        //2.根据专辑ID（等值）+序号（大于等于）得到“待购买”声音列表（可能包含用户已购声音）
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, albumId);
        queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
        queryWrapper.select(TrackInfo::getId);
        List<TrackInfo> userWaitBuyTrackList = trackInfoMapper.selectList(queryWrapper);
        Assert.notNull(userWaitBuyTrackList, "不存在待购声音");

        //3.远程调用“用户服务”得到已购声音ID列表
        //3.1 获取“待购买”声音ID列表（可能包含用户已购声音ID）
        List<Long> userWaitBuyTrackIdList = userWaitBuyTrackList.stream().map(TrackInfo::getId).collect(Collectors.toList());
        //3.2 远程调用用户服务得到已购声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();
        //4.将"待购买"声音列表中用户已购声音排除掉得到真正“待购买”声音列表
        if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
            userWaitBuyTrackIdList = userWaitBuyTrackIdList
                    .stream()
                    .filter(userWaitBuyTrackId -> !userPaidTrackIdList.contains(userWaitBuyTrackId))
                    .collect(Collectors.toList());
        }
        //5.根据待购声音列表长度构建分集购买对象集合
        List<Map<String, Object>> list = new ArrayList<>();
        int count = userWaitBuyTrackIdList.size();
        //5.1 获取声音价格（专辑中价格）
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        BigDecimal price = albumInfo.getPrice();
        //5.1 构建本集分集购买对象
        Map<String, Object> currentMap = new HashMap<>();
        currentMap.put("name", "本集");
        currentMap.put("price", price);
        currentMap.put("trackCount", 1);
        list.add(currentMap);
        //5.2 构建其他分集购买对象 count=25 本集 后10集 后20集 全集   cuont=20 本集 后10集 全集   count=45 本集 后10|20|30|40集 全集
        //第一次循环i=10 第二次循环i=20
        for (int i = 10; i <= 50; i += 10) {
            if (count > i) {
                //待购买声音数量>i 显示 后i集
                Map<String, Object> map = new HashMap<>();
                map.put("name", "后" + i + "集");
                map.put("price", price.multiply(BigDecimal.valueOf(i)));
                map.put("trackCount", i);
                list.add(map);
            } else {
                //显示 全集
                Map<String, Object> map = new HashMap<>();
                map.put("name", "全（" + count + ")集");
                map.put("price", price.multiply(BigDecimal.valueOf(count)));
                map.put("trackCount", count);
                list.add(map);
                break;
            }
        }
        return list;
    }

    /**
     * 基于选择购买声音ID返回当前用户待结算声音列表
     *
     * @param userId     用户ID
     * @param trackId    选中声音ID（起始购买声音ID）
     * @param trackCount 购买数量
     * @return 与购买数量对应数量声音集合
     */
    @Override
    public List<TrackInfo> getWaitBuyTrackInfoList(Long userId, Long trackId, int trackCount) {
        //1.根据选中购买声音ID得到声音所属专辑ID&序号
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Long albumId = trackInfo.getAlbumId();
        Integer orderNum = trackInfo.getOrderNum();

        //2.远程调用“用户服务”得到已购声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();

        //3.根据专辑ID（等值）&序号（大于等于）& 声音ID（不存在已购声音ID列表），序号（升序）限制trackCount条返回待结算声音列表
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 3.1 专辑等值查询
        queryWrapper.eq(TrackInfo::getAlbumId, albumId);
        // 3.1 序号大于等于用户选择购买声音序号
        queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
        if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
            //说明该专辑下存在已购声音
            queryWrapper.notIn(TrackInfo::getId, userPaidTrackIdList);
        }
        //todo limit 后加空格 3.3 限制返回数量
        queryWrapper.last("limit " + trackCount);
        //3.4 按照序号进行升序
        queryWrapper.orderByAsc(TrackInfo::getOrderNum);
        //3.5 指定返回列
        queryWrapper.select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getAlbumId, TrackInfo::getCoverUrl);
        List<TrackInfo> trackInfoList = trackInfoMapper.selectList(queryWrapper);
        return trackInfoList;
    }

}
